package com.gw.vip.server.service;

import com.gw.vip.common.exception.ServiceException;
import com.gw.vip.server.bo.TransAccSumBo;
import com.gw.vip.server.po.*;
import com.gw.vip.server.service.dao_service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @author GW
 * @description
 * @since 2022-05-07
 */
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class DayCutBiz {

    private final BsOrderService bsOrderService;
    private final TransService transService;
    private final TransLockService transLockService;
    private final TmpOrderService tmpOrderService;
    private final TmpTransService tmpTransService;
    private final TmpTransLockService tmpTransLockService;
    private final AccountService accountService;
    private final BatchClearLedgerService batchClearLedgerService;
    private final HisOrderService hisOrderService;
    private final HisTransService hisTransService;
    private final HisTransLockService hisTransLockService;
    private final SysArgService sysArgService;

    /**
     * 设置清算日期
     * @param clearDate
     * @param clearMaxTime
     */
    public void setClearDate(LocalDate clearDate, LocalDateTime clearMaxTime) {
        log.info("------------------设置清算日期【{}】-----开始---------------------", clearDate);

        //查看清算日的交易有没有初始的交易。
        int transNum = bsOrderService.getInOriCount(clearMaxTime);
        if (transNum>0){
            log.error("清算日【{}】内有未知交易，不准清结算！", clearDate);
            throw new ServiceException("清算日内有初始交易，无法清结算！");
        }

        //trans设置清算日期
        bsOrderService.setClearDate(clearDate, clearMaxTime);

        //transAcc设置清算日期
        transService.setClearDate(clearDate, clearMaxTime);

        log.info("------------------设置清算日期【{}】-----结束---------------------", clearDate);
    }

    /**
     * 初始化tmp表
     * @param clearDate
     */
    public void initTmp(LocalDate clearDate) {
        log.info("------------------初始化tmp表【{}】-----开始---------------------", clearDate);

        tmpOrderService.truncate();
        int num = tmpOrderService.initData(clearDate);
        if (num==0){
            //从历史表初始化
            tmpOrderService.initDataFromHis(clearDate);
        }

        tmpTransService.truncate();
        num = tmpTransService.initData(clearDate);
        if (num==0){
            //从历史表初始化
            tmpTransService.initDataFromHis(clearDate);
        }

        log.info("------------------初始化tmp表【{}】-----结束---------------------", clearDate);
    }

    /**
     * 帐户明细检查
     * @param clearDate
     */
    public void checkAccountTrans(LocalDate clearDate) {
        log.info("************************ 清算日期【{}】 ******* 帐户明细检查 *** 开始 ************************", clearDate);

        List<Account> accountList = accountService.listByChange();
        for (Account account:accountList){
            log.info("----------------------检查帐户【{}】-------------------开始----------------------", account.getId());
            BigDecimal diffAmtByAccount = account.getClearAmt().subtract(account.getLastAmt());
            BigDecimal diffAmtByTrans = tmpTransService.getDiffAmtByAccount(account.getId(), clearDate);
            if (diffAmtByTrans==null||diffAmtByAccount.compareTo(diffAmtByTrans)!=0){
                log.error("帐户【{}】,清算日期【{}】交易明细不平衡", account.getId(), clearDate);
                throw new ServiceException("帐户明细检查失败！");
            }
            log.info("----------------------检查帐户【{}】-------------------结束----------------------", account.getId());
        }

        log.info("************************ 清算日期【{}】 ******* 帐户明细检查 *** 结束 ************************", clearDate);
    }

    public void checkAllAmt(LocalDate clearDate) {
        log.info("************************ 清算日期【{}】 ******* 总分平衡检查 *** 开始 ************************", clearDate);

        //总账统计
        List<BatchClearLedger> clearLedgerList = accountService.getLedger();
        for (BatchClearLedger clearLedger:clearLedgerList){
            BatchClearLedger srcClearLedger = batchClearLedgerService.getByBase(clearLedger.getCapitalId());
            if (srcClearLedger == null){
                //新建
                srcClearLedger = batchClearLedgerService.init(clearLedger, clearDate);
                batchClearLedgerService.save(srcClearLedger);
            }else{
                //更新
                srcClearLedger.setAmt(clearLedger.getAmt());
                srcClearLedger.setDiffAmt(srcClearLedger.getAmt().subtract(srcClearLedger.getLastAmt()));
                srcClearLedger.setClearDate(clearDate);
                srcClearLedger.setCrtTime(LocalDateTime.now());
                srcClearLedger.setUpdTime(srcClearLedger.getCrtTime());
                batchClearLedgerService.updateById(srcClearLedger);
            }
        }

        clearLedgerList = batchClearLedgerService.list();

        //清算日期内的trans统计
        List<TransAccSumBo> transAccSumBoList = tmpTransService.transSum(clearDate);
        BigDecimal transAccSumAmt = transAccSumBoList.stream().map(TransAccSumBo::getSumAmt).reduce(BigDecimal.ZERO, BigDecimal::add);
        //清算日期内的trans统计
        BigDecimal transSumAmt = tmpOrderService.orderSum(clearDate);

        //检查trans_acc和trans的总额是否一致
        if (transAccSumAmt.compareTo(transSumAmt)!=0){
            log.error("清算日期【{}】order总额【{}】,trans总额【{}】,不一致！", clearDate, transSumAmt, transAccSumAmt);
            throw new ServiceException("清算日期的order总额和trans总额不一致");
        }

        for (BatchClearLedger clearLedger:clearLedgerList){
            log.info("-----------------检查capitalId【{}】----开始------", clearLedger.getCapitalId());

            BigDecimal sumAmt = this.getSumAmt(transAccSumBoList, clearLedger.getCapitalId());
            if (sumAmt==null){
                sumAmt=BigDecimal.ZERO;
            }

            if (sumAmt.compareTo(clearLedger.getDiffAmt())!=0){
                log.error("capitalId【{}】帐户总分不平衡。帐户变动【{}】,交易记录变动【{}】,以充值为正值。", clearLedger.getCapitalId(), clearLedger.getDiffAmt(), sumAmt);
                throw new ServiceException("总分平衡检查失败！");
            }

            log.info("-----------------检查capitalId【{}】----结束------", clearLedger.getCapitalId());
        }

        //总账转历史
        batchClearLedgerService.toHis();

        //修改总账上日金额，并初始化diff_amt
        batchClearLedgerService.updateLastAmt();

        log.info("************************ 清算日期【{}】 ******* 总分平衡检查 *** 结束 ************************", clearDate);
    }

    public void toHis(LocalDate clearDate) {
        log.info("************************ 清算日期【{}】 ******* 交易转历史 *** 开始 ************************", clearDate);

        this.orderToHis(clearDate);
        this.transToHis(clearDate);

        log.info("************************ 清算日期【{}】 ******* 交易转历史 *** 结束 ************************", clearDate);
    }

    public void nextClearReady(LocalDate clearDate) {
        log.info("************************ 清算日期【{}】 ******* 下一个清算准备 *** 开始 ************************", clearDate);

        //设置上日余额
        List<Account> lastAccountList = accountService.listByLast();
        for (Account account : lastAccountList){
            int i = accountService.updateLastAmt(account.getId());
            if(i!=1){
                log.error("帐户【{}】更新失败！", account.getId());
                throw new ServiceException("账户更新失败！");
            }
        }

        //清算日期加1
        sysArgService.setNextClearDate(clearDate);

        //设置清算金额
        List<Account> accountList = accountService.listByClearReady();
        for (Account account : accountList){
            //更新清算金额
            int i = accountService.updateClearAmt(account.getId());
            if(i!=1){
                log.error("帐户【{}】更新失败！", account.getId());
                throw new ServiceException("账户更新失败！");
            }
        }

        log.info("************************ 清算日期【{}】 ******* 下一个清算准备 *** 结束 ************************", clearDate);
    }

    //------------------------------ 辅助函数 -------------------------------//

    private BigDecimal getSumAmt(List<TransAccSumBo> transAccSumBoList, String capitalId){
        if (transAccSumBoList == null){
            log.error("transSumBoList为空！");
            throw new ServiceException("系统异常!");
        }

        BigDecimal sumAmt = BigDecimal.ZERO;

        for (TransAccSumBo transAccSumBo : transAccSumBoList){
            if (!transAccSumBo.getCapitalId().equals(capitalId)){
                continue;
            }
            sumAmt = transAccSumBo.getSumAmt();
            break;
        }

        return sumAmt;
    }

    /**
     * trans转历史
     * @param clearDate
     */
    private void orderToHis(LocalDate clearDate) {
        //检查his_trans表是否已经存在记录
        int num = hisOrderService.countByClearDate(clearDate);
        if (num>0){
            //已经转历史，无须重复执行
            log.info("----------------- 已经转历史，无须重复执行！----- 清算日期【{}】----", clearDate);
            return;
        }

        //将tmp_trans中的交易转入历史
        hisOrderService.tmpTransToHis(clearDate);

        //删除trans表中交易
        List<TmpOrder> tmpOrderList = tmpOrderService.listByClearDate(clearDate);
        for (TmpOrder tmpOrder : tmpOrderList){
            bsOrderService.deleteById(tmpOrder.getId());
        }
    }

    /**
     * transAcc转历史
     * @param clearDate
     */
    private void transToHis(LocalDate clearDate) {
        //检查his_trans_acc表是否已经存在记录
        int num = hisTransService.countByClearDate(clearDate);
        if (num>0){
            //已经转历史，无须重复执行
            log.info("----------------- 已经转历史，无须重复执行！----- 清算日期【{}】----", clearDate);
            return;
        }

        //将tmp_trans中的交易转入历史
        hisTransService.tmpTransToHis(clearDate);

        //删除trans表中交易
        List<TmpTrans> tmpTransList = tmpTransService.listByClearDate(clearDate);
        for (TmpTrans tmpTrans : tmpTransList){
            transService.deleteById(tmpTrans.getId());
        }
    }
}
